using BusinessLogic.BusinessEntity.Entities;
using BusinessLogic.Managers.Processing.ProcessingExceptions;
using BusinessLogic.Mappers;
using BusinessLogic.Mappers.CurrencyConversion;
using DataAccess.Services;
using BusinessLogic.Mappers.AccountCode;

namespace BusinessLogic.Managers.Processing.RemittanceAdvice.ValidationRules
{
	internal class RemittanceAdviceAccountingValidationRule : IRemittanceAdviceValidationRule
	{
		public void Validate(BusinessEntity.Entities.RemittanceAdvice document, IServiceManager serviceManager)
		{
			IAccountMapper accountMapper = MapperFactory.GetAccountMapper(serviceManager);
			ICurrencyConversionMapper currencyConversionMapper = MapperFactory.GetCurrencyConversionMapper(serviceManager);

			decimal currencyBasedAmountToPay = GetCurrencyBasedAmount(document.TotalAmount, document.Currency, document.PayerPartyAccount.Currency, currencyConversionMapper);

			if (document.PayerPartyAccount.Balance < currencyBasedAmountToPay)
			{
				throw new LowBalanceException();
			}

			CalculateNettingAndUpdateAccounts(document, accountMapper, currencyConversionMapper, currencyBasedAmountToPay, serviceManager);
		}

		private static decimal GetCurrencyBasedAmount(decimal initialAmount, Currency sourceCurrency, Currency targetCurrency, ICurrencyConversionMapper currencyConversionMapper)
		{
			if(sourceCurrency.ID == targetCurrency.ID)
			{
				return initialAmount;
			}

			decimal conversionRate = currencyConversionMapper.Get(sourceCurrency.Code, targetCurrency.Code).Value;
			
			return decimal.Multiply(initialAmount, conversionRate);
		}

		private static void CalculateNettingAndUpdateAccounts(BusinessEntity.Entities.RemittanceAdvice document, IAccountMapper accountMapper, ICurrencyConversionMapper currencyConversionMapper, decimal currencyBasedAmountToPay, IServiceManager serviceManager)
		{
			decimal currencyBasedAmountToReceive = GetCurrencyBasedAmount(currencyBasedAmountToPay, document.PayerPartyAccount.Currency, document.BillToPartyAccount.Currency, currencyConversionMapper);

			document.PayerPartyAccount.Balance -= currencyBasedAmountToPay;
			document.BillToPartyAccount.Balance += currencyBasedAmountToReceive;

			accountMapper.UpdateBalance(document.PayerPartyAccount.ID, document.PayerPartyAccount.Balance);
			accountMapper.UpdateBalance(document.BillToPartyAccount.ID, document.BillToPartyAccount.Balance);
		}
	}
}